# -*- coding: utf-8 -*-

'''定制类'''
'''
定制类
如果要把一个类的实例变成 str，就需要实现特殊方法__str__()：
直接对学习的List进行调用，似乎__str__() 不会被调用,打印出[<__main__.Student object at 0x026132B0>, <__main__.Student object at 0x026132D0>, <__main__.Student object at 0x02613310>]
使用 __repr__ = __str__后，打印出[(Student: Tim, male, 99), (Student: Bob, male, 88), (Student: Alice, male, 77)]
因为 Python 定义了__str__()和__repr__()两种方法，__str__()用于显示给用户，而__repr__()用于显示给开发人员。
对 int、str 等内置数据类型排序时，Python的 sorted() 按照默认的比较函数 cmp 排序，但是，如果对一组 Student 类的实例排序时，就必须提供我们自己的特殊方法 __cmp__()
如果一个类表现得像一个list，要获取有多少个元素，就得用 len() 函数

Python 提供的基本数据类型 int、float 可以做整数和浮点的四则运算以及乘方等运算。但是，四则运算不局限于int和float，还可以是有理数、矩阵等。要表示有理数，可以用一个Rational类来表示：

装饰器函数，可以用装饰器函数把 get/set 方法“装饰”成属性调用
第一个score(self)是get方法，用@property装饰，第二个score(self, score)是set方法，用@score.setter装饰，@score.setter是前一个@property装饰后的副产品。
__slots__的目的是限制当前类所能拥有的属性，如果不需要添加任意动态的属性，使用__slots__也能节省内存。
在Python中，函数其实是一个对象：所有的函数都是可调用对象。一个类实例也可以变成一个可调用对象，只需要实现一个特殊方法__call__()
'''
print '如果要把一个类的实例变成 str，就需要实现特殊方法__str__()'
class Person(object):
    def __init__(self, name, gender):
        self.name = name
        self.gender = gender
    def __str__(self):
        return '(Person: %s, %s)' % (self.name, self.gender)
    def __call__(self, friend):
        print 'My name is %s...' % self.name
        print 'My friend is %s...' % friend
p = Person('Bob', 'male')
print p

class Student(Person):
    __slots__ = ('name', 'gender', 'score')
    def __init__(self, name, gender, score):
        super(Student, self).__init__(name, gender)
        self.score = score
    @property
    def score(self):
        return self.__score
    @score.setter
    def score(self, score):
        if score < 0 or score > 100:
            raise ValueError('invalid score')
        self.__score = score
    def __str__(self):
        return '(Student: %s, %s, %s)' % (self.name, self.gender, self.score)
    def __cmp__(self, s):
        if self.score == s.score:
            return cmp(self.name, s.name)
        return -cmp(self.score, s.score)
    __repr__ = __str__ #去掉之后，下面L打印出的是地址

s = Student('Bobson', 'male',89)
print s
L = [Student('Tim', 'male',99), Student('Bob', 'male',88), Student('Alice', 'male',77)]
print L
print sorted(L)

#python中有理数的数学运算
def gcd(a, b):
    if b == 0:
        return a
    return gcd(b, a % b)

class Rational(object):
    def __init__(self, p, q):
        self.p = p
        self.q = q
    #Rational有理数进行+运算
    def __add__(self, r):
        return Rational(self.p * r.q + self.q * r.p, self.q * r.q)
    #Rational有理数进行-运算
    def __sub__(self, r):
        return Rational(self.p * r.q - self.q * r.p, self.q * r.q)
    # Rational有理数进行*运算
    def __mul__(self, r):
        return Rational(self.p * r.p , self.q * r.q)
    # Rational有理数进行除法运算
    def __div__(self, r):
        return Rational(self.p * r.q, self.q * r.p)
    #转成整数
    def __int__(self):
        return self.p // self.q
    #转成单精度
    def __float__(self):
        return float(self.p) / self.q
    def __str__(self):
        g = gcd(self.p, self.q)
        return '%s/%s' % (self.p / g, self.q / g)
    __repr__ = __str__
r1 = Rational(2, 3)
r2 = Rational(3, 4)
print r1 + r2
print r1 - r2
print r1 * r2
print r1 / r2
print float(r1)
print int(r1)

#python中 @property
'第一个score(self)是get方法，用@property装饰，第二个score(self, score)是set方法，用@score.setter装饰，@score.setter是前一个@property装饰后的副产品。'
#s.score=199
s.grade = 'A' #__slots__的目的是限制当前类所能拥有的属性，如果不需要添加任意动态的属性，使用__slots__也能节省内存。
print s.grade
print '在Python中，函数其实是一个对象：所有的函数都是可调用对象。一个类实例也可以变成一个可调用对象，只需要实现一个特殊方法__call__()'
f = abs
f.__name__
s('Tom') #s('Tim') 你无法确定 s是一个函数还是一个类实例，所以，在Python中，函数也是对象，对象和函数的区别并不显著。
print '斐波那契数列'
class Fib(object):
    def __call__(self, num):
        a, b, L = 0, 1, []
        for n in range(num):
            L.append(a)
            a, b = b, a + b
        return L
f = Fib()
print f(10)

